﻿#include "libcomm.h"

// Initialized static value.
//
const CommType CommType::Void;
const CommType CommType::Null = (void*)0;
const CommType CommType::Error = (void*)-1;

CommType::CommType(void) { m_t = CT_VOID; memset(&m_v, 0, sizeof(m_v)); }
CommType::CommType(const void* ptr) { m_t = CT_POINTER; m_v.p = (char*)ptr; }
CommType::CommType(float f) { m_t = CT_FLOAT; m_v.f = f; }
CommType::CommType(double lf) { m_t = CT_DOUBLE; m_v.lf = lf; }
CommType::CommType(unsigned char b) { m_t = CT_BYTE; m_v.b = b; }
CommType::CommType(bool b) { m_t = CT_BYTE; m_v.b = b ? 1 : 0; }
CommType::CommType(short s) { m_t = CT_SHORT; m_v.s = s; }
CommType::CommType(int i) { m_t = CT_INT; m_v.i = i; }
CommType::CommType(long l) { m_t = CT_LONG; m_v.l = l; }
CommType::CommType(long long ll) { m_t = CT_LONGLONG; m_v.ll = ll; }
CommType::CommType(unsigned short us) { m_t = CT_USHORT; m_v.us = us; }
CommType::CommType(unsigned int ui) { m_t = CT_UINT; m_v.ui = ui; }
CommType::CommType(unsigned long ul) { m_t = CT_ULONG; m_v.ul = ul; }
CommType::CommType(unsigned long long ull) { m_t = CT_ULONGLONG; m_v.ull = ull; }
CommType::CommType(const Buffer& buffer) { Copy(buffer.get(), buffer.size(), CT_BUFFER); }
CommType::CommType(const void* buffer, size_t size) { Copy(buffer, size, CT_BUFFER); }
CommType::CommType(const char* str) { Copy(str, ((NULL == str) ? 0 : (strlen(str) + 1)), CT_STRING); }
CommType::CommType(const String& str) { Copy(str.get(), str.size(), CT_STRING); }
CommType::CommType(const std::string& str) { Copy(str.c_str(), ((str.empty()) ? 0 : (str.length() + 1)), CT_STRING); }
CommType::CommType(const CommType& rhs) { Copy(rhs); }
CommType& CommType::operator=(const CommType& rhs)
{
	if (rhs.Type() != this->Type() || rhs.get() != this->get())
	{
		Release();
		Copy(rhs);
	}
	return *this;
}
void CommType::Copy(const void* buffer, size_t size, ValueType type)
{
	MYLIB_ASSERT_NOTNULL(CT_BUFFER == type || CT_STRING == type);
	m_t = type;
	m_p_size = size;
	m_v.p = NULL;
	if (m_p_size > 0 && NULL != buffer)
	{
		m_v.p = (char*)Buffer::malloc(m_p_size);
		MYLIB_ASSERT_NOTNULL(m_v.p);
		memcpy(m_v.p, buffer, m_p_size);
	}
}
void CommType::Copy(const CommType& rhs)
{
	if (CT_BUFFER == rhs.m_t || CT_STRING == rhs.m_t)
	{
		Copy(rhs.get(), rhs.size(), rhs.m_t);
	}
	else
	{
		m_t = rhs.m_t;
		m_v = rhs.m_v;
	}
}
void CommType::Release()
{
	if (CT_BUFFER == m_t || CT_STRING == m_t)
	{
		Buffer::free(m_v.p);
		m_p_size = 0;
	}
	// set to CommType::Null.
	memset(&m_v, 0, sizeof(m_v));
	m_t = CT_POINTER;
}
CommType::~CommType() { Release(); }

CommType::ValueType CommType::Type() const { return m_t; }

const char* CommType::TypeString() const
{
	static const char* TYPESTRING[] = {
		"void",		"pointer",	"buffer",	"string",	"float",	"double",	"byte",
		"short",	"int",		"long",		"long long",
		"unsigned short",	"unsigned int",	"unsigned long",	"unsigned long long"
	};
	return TYPESTRING[m_t];
}

void* CommType::ptr() const
{
	void* ptr = NULL;
	switch (m_t)
	{
	case CT_STRING:
	case CT_BUFFER:
	case CT_POINTER:
		ptr = (void*)m_v.p;
		break;
	case CT_FLOAT:
		ptr = (void*)&m_v.f;
		break;
	case CT_DOUBLE:
		ptr = (void*)&m_v.lf;
		break;
	case CT_BYTE:
		ptr = (void*)&m_v.b;
		break;
	case CT_SHORT:
		ptr = (void*)&m_v.s;
		break;
	case CT_INT:
		ptr = (void*)&m_v.i;
		break;
	case CT_LONG:
		ptr = (void*)&m_v.l;
		break;
	case CT_LONGLONG:
		ptr = (void*)&m_v.ll;
		break;
	case CT_USHORT:
		ptr = (void*)&m_v.us;
		break;
	case CT_UINT:
		ptr = (void*)&m_v.ui;
		break;
	case CT_ULONG:
		ptr = (void*)&m_v.ul;
		break;
	case CT_ULONGLONG:
		ptr = (void*)&m_v.ull;
		break;
	default:
		break;
	}
	return ptr;
}

void* CommType::get()
{
	return ptr();
}

const void* CommType::get() const
{
	return ptr();
}

size_t CommType::size() const
{
	switch (m_t)
	{
	case CT_STRING:
	case CT_BUFFER:
		return m_p_size;
	case CT_POINTER:
		return sizeof(m_v.p);
	case CT_FLOAT:
		return sizeof(m_v.f);
	case CT_DOUBLE:
		return sizeof(m_v.lf);
	case CT_BYTE:
		return sizeof(m_v.b);

	case CT_SHORT:
		return sizeof(m_v.s);
	case CT_INT:
		return sizeof(m_v.i);
	case CT_LONG:
		return sizeof(m_v.l);
	case CT_LONGLONG:
		return sizeof(m_v.ll);

	case CT_USHORT:
		return sizeof(m_v.us);
	case CT_UINT:
		return sizeof(m_v.ui);
	case CT_ULONG:
		return sizeof(m_v.ul);
	case CT_ULONGLONG:
		return sizeof(m_v.ull);
	default:
		break;
	}
	return 0;
}

bool CommType::Equal(const CommType& rhs) const
{
	if (m_t != rhs.m_t)
		return false;

	switch (m_t)
	{
	case CT_VOID:
		return true;
	case CT_STRING:
		return (0 == strcmp(m_v.p, rhs.m_v.p));
	case CT_BUFFER:
		return (this->size() == rhs.size() && 0 == memcmp(m_v.p, rhs.m_v.p, rhs.size()));
	case CT_POINTER:
		return (m_v.p == rhs.m_v.p);
	case CT_FLOAT:
		return (m_v.f == rhs.m_v.f);
	case CT_DOUBLE:
		return (m_v.lf == rhs.m_v.lf);
	case CT_BYTE:
		return (m_v.b == rhs.m_v.b);

	case CT_SHORT:
		return (m_v.s == rhs.m_v.s);
	case CT_INT:
		return (m_v.i == rhs.m_v.i);
	case CT_LONG:
		return (m_v.l == rhs.m_v.l);
	case CT_LONGLONG:
		return (m_v.ll == rhs.m_v.ll);

	case CT_USHORT:
		return (m_v.us == rhs.m_v.us);
	case CT_UINT:
		return (m_v.ui == rhs.m_v.ui);
	case CT_ULONG:
		return (m_v.ul == rhs.m_v.ul);
	case CT_ULONGLONG:
		return (m_v.ull == rhs.m_v.ull);
	default:
		break;
	}
	return false;
}

bool CommType::IsVoid() const
{
	return this->Equal(CommType::Void);
}

bool CommType::IsNull() const
{
	return this->Equal(CommType::Null);
}

bool CommType::IsError() const
{
	return this->Equal(CommType::Error);
}

bool CommType::operator==(const CommType& rhs) const
{
	return this->Equal(rhs);
}

bool CommType::operator!=(const CommType& rhs) const
{
	return !this->Equal(rhs);
}

CommType::operator String() const { MYLIB_ASSERT(CT_STRING == m_t); return m_v.p; }
CommType::operator std::string() const { MYLIB_ASSERT(CT_STRING == m_t); return m_v.p; }
CommType::operator Buffer() const { MYLIB_ASSERT(CT_BUFFER == m_t); return Buffer(m_v.p, m_p_size); }
CommType::operator void*() { return get(); }
CommType::operator const void*() const { return get(); }
CommType::operator char*() { MYLIB_ASSERT(CT_STRING == m_t); return m_v.p; }
CommType::operator const char*() const { MYLIB_ASSERT(CT_STRING == m_t); return m_v.p; }
CommType::operator float() const { MYLIB_ASSERT(CT_FLOAT == m_t); return m_v.f; }
CommType::operator double() const
{
	MYLIB_ASSERT(CT_DOUBLE == m_t || CT_FLOAT == m_t);
	switch (m_t)
	{
	case CT_FLOAT:
		return m_v.f;
	default:
		break;
	}
	return m_v.lf;
}
CommType::operator unsigned char() const
{
	MYLIB_ASSERT(CT_BYTE == m_t);
	return m_v.b;
}
CommType::operator bool() const
{
	switch (m_t)
	{
	case CT_VOID:
		return false;
	case CT_POINTER:
	case CT_BUFFER:
	case CT_STRING:
		return (NULL != m_v.p);
	case CT_FLOAT:
		return (0 != m_v.f);
	case CT_DOUBLE:
		return (0 != m_v.lf);
	case CT_BYTE:
		return (0 != m_v.b);

	case CT_SHORT:
		return (0 != m_v.s);
	case CT_INT:
		return (0 != m_v.i);
	case CT_LONG:
		return (0 != m_v.l);
	case CT_LONGLONG:
		return (0 != m_v.ll);

	case CT_USHORT:
		return (0 != m_v.us);
	case CT_UINT:
		return (0 != m_v.ui);
	case CT_ULONG:
		return (0 != m_v.ul);
	case CT_ULONGLONG:
		return (0 != m_v.ull);
	default:
		break;
	}
	return false;
}
CommType::operator short() const
{
	MYLIB_ASSERT(CT_SHORT == m_t || CT_BYTE == m_t);
	switch (m_t)
	{
	case CT_BYTE:
		return m_v.b;
	default:
		break;
	}
	return m_v.s;
}
CommType::operator int() const
{
	MYLIB_ASSERT(CT_INT == m_t || CT_SHORT == m_t || CT_BYTE == m_t);
	switch (m_t)
	{
	case CT_SHORT:
		return m_v.s;
	case CT_BYTE:
		return m_v.b;
	default:
		break;
	}
	return m_v.i;
}
CommType::operator long() const
{
	MYLIB_ASSERT(CT_LONG == m_t || CT_INT == m_t || CT_SHORT == m_t || CT_BYTE == m_t);
	switch (m_t)
	{
	case CT_INT:
		return m_v.i;
	case CT_SHORT:
		return m_v.s;
	case CT_BYTE:
		return m_v.b;
	default:
		break;
	}
	return m_v.l;
}
CommType::operator long long() const
{
	MYLIB_ASSERT(CT_LONGLONG == m_t || CT_LONG == m_t || CT_INT == m_t || CT_SHORT == m_t || CT_BYTE == m_t);
	switch (m_t)
	{
	case CT_LONG:
		return m_v.l;
	case CT_INT:
		return m_v.i;
	case CT_SHORT:
		return m_v.s;
	case CT_BYTE:
		return m_v.b;
	default:
		break;
	}
	return m_v.ll;
}
CommType::operator unsigned short() const
{
	MYLIB_ASSERT(CT_USHORT == m_t || CT_BYTE == m_t);
	switch (m_t)
	{
	case CT_BYTE:
		return m_v.b;
	default:
		break;
	}
	return m_v.us;
}
CommType::operator unsigned int() const
{
	MYLIB_ASSERT(CT_UINT == m_t || CT_USHORT == m_t || CT_BYTE == m_t);
	switch (m_t)
	{
	case CT_USHORT:
		return m_v.us;
	case CT_BYTE:
		return m_v.b;
	default:
		break;
	}
	return m_v.ui;
}
CommType::operator unsigned long() const
{
	MYLIB_ASSERT(CT_ULONG == m_t || CT_UINT == m_t || CT_USHORT == m_t || CT_BYTE == m_t);
	switch (m_t)
	{
	case CT_UINT:
		return m_v.ui;
	case CT_USHORT:
		return m_v.us;
	case CT_BYTE:
		return m_v.b;
	default:
		break;
	}
	return m_v.ul;
}
CommType::operator unsigned long long() const
{
	MYLIB_ASSERT(CT_ULONGLONG == m_t || CT_ULONG == m_t || CT_UINT == m_t || CT_USHORT == m_t || CT_BYTE == m_t);
	switch (m_t)
	{
	case CT_ULONG:
		return m_v.ul;
	case CT_UINT:
		return m_v.ui;
	case CT_USHORT:
		return m_v.us;
	case CT_BYTE:
		return m_v.b;
	default:
		break;
	}
	return m_v.ull;
}

String CommType::ToString() const
{
	if (CT_STRING == m_t)
	{
		return m_v.p;
	}
	else if (CT_BUFFER == m_t)
	{
		if (NULL == m_v.p || m_p_size == 0)
			return NULL;
		for (size_t i = m_p_size - 1; i >= 0; i--)
		{
			if (m_v.p[i] == 0)
				return m_v.p; // have '\0' ended. 
		}
		MYLIB_ASSERT(!"can't convert no-zero-ended buffer to string!");
	}
	else
	{
		String str(32);
		switch (m_t)
		{
		case CT_POINTER:
			return str.sprintf("%p", (void*)m_v.p);
		case CT_FLOAT:
			return str.sprintf("%f", m_v.f);
		case CT_DOUBLE:
			return str.sprintf("%lf", m_v.lf);
		case CT_BYTE:
			return str.sprintf("%d", m_v.b);

		case CT_SHORT:
			return str.sprintf("%hd", m_v.s);
		case CT_INT:
			return str.sprintf("%d", m_v.i);
		case CT_LONG:
			return str.sprintf("%ld", m_v.l);
		case CT_LONGLONG:
			return str.sprintf("%lld", m_v.ll);

		case CT_USHORT:
			return str.sprintf("%hu", m_v.us);
		case CT_UINT:
			return str.sprintf("%u", m_v.ui);
		case CT_ULONG:
			return str.sprintf("%lu", m_v.ul);
		case CT_ULONGLONG:
			return str.sprintf("%llu", m_v.ull);
		default:
			break;
		}
	}
	return NULL;
}

// "void*,long,unsigned long,size_t" have different size on 32/64bit system.
Buffer CommType::Serialize() const
{
	const void* ptr = this->get();
	ValueType type_ = this->Type();
	size_t size_ = this->size();
	MYLIB_ASSERT(size_ == 0 || NULL != ptr);
	MYLIB_ASSERT((int)type_ <= 0x7F);
	MYLIB_ASSERT(size_ <= COMMTYPE_MAX_SIZE); // must be less than max value of signed int.
	unsigned char type = type_;
	unsigned int size = size_;
	int type_size = sizeof(type);	// 1 bytes.
	int size_size = sizeof(size);	// 4 bytes even size_t in 64bit.
	if (CT_POINTER == type_)
	{
		ptr = &m_v.p;
		size = sizeof(m_v.p);
	}

	Buffer buffer(type_size + size_size + size);
	memcpy((char*)buffer, &type, type_size);						// 1 bytes type.
	memcpy((char*)buffer + type_size, &size, size_size);			// 4 bytes size.
	memcpy((char*)buffer + type_size + size_size, ptr, size);	// value buffer.
	return buffer;
}

CommType CommType::Deserialize(const void* buffer, size_t sz)
{
	unsigned char type;
	unsigned int size;
	unsigned int type_size = sizeof(type);	// 1 bytes.
	unsigned int size_size = sizeof(size);	// 4 bytes.
	const char* ptr = (const char*)buffer;
	size_t size_ = sz;
	if (size_ < type_size + size_size)
	{
		LOGERR("%s(line %d) error: invalid buffer header(size:0x%x).", __FUNCTION__, __LINE__, size_);
		return CommType::Error;
	}

	memcpy(&type, ptr, type_size);
	ptr += type_size;
	memcpy(&size, ptr, size_size);
	ptr += size_size;
	if (type >= CT_MAX || size > COMMTYPE_MAX_SIZE)
	{
		LOGERR("%s(line %d) error: invalid buffer header(type:%d,size:0x%x).", __FUNCTION__, __LINE__, type, size);
		return CommType::Error;
	}
	if (type_size + size_size + size != size_)
	{
		LOGERR("%s(line %d) error: invalid buffer size(0x%x!=0x%x).", __FUNCTION__, __LINE__,
			type_size + size_size + size, size_);
		return CommType::Error;
	}

	switch (type)
	{
	case CT_VOID:
	{
		if (size == 0)
			return CommType::Void;
	}
	case CT_STRING:
	{
		if (size == 0) 
			return (char*)NULL;
		if (ptr[size - 1] == 0) // not ended by '\0'.
			return ptr;
	}
	case CT_BUFFER:
	{
		return Buffer(ptr, size);
	}
	case CT_POINTER:
	{
		if (size == sizeof(void*))
			return *((void**)ptr);
	}
	case CT_FLOAT:
	{
		if (size == sizeof(float))
			return *((float*)ptr);
	}
	case CT_DOUBLE:
	{
		if (size == sizeof(double))
		return *((double*)ptr);
	}
	case CT_BYTE:
	{
		if (size == sizeof(unsigned char))
			return *((unsigned char*)ptr);
	}
	case CT_SHORT:
	{
		if (size == sizeof(short))
			return *((short*)ptr);
	}
	case CT_INT:
	{
		if (size == sizeof(int))
			return *((int*)ptr);
	}
	case CT_LONG:
	{
		if (size == sizeof(long))
			return *((long*)ptr);
	}
	case CT_LONGLONG:
	{
		if (size == sizeof(long long))
			return *((long long*)ptr);
	}
	case CT_USHORT:
	{
		if (size == sizeof(unsigned short))
			return *((unsigned short*)ptr);
	}
	case CT_UINT:
	{
		if (size == sizeof(unsigned int))
			return *((unsigned int*)ptr);
	}
	case CT_ULONG:
	{
		if (size == sizeof(unsigned long))
			return *((unsigned long*)ptr);
	}
	case CT_ULONGLONG:
	{
		if (size == sizeof(unsigned long long))
			return *((unsigned long long*)ptr);
	}
	default:
		break;
	}

	return CommType::Error;
}

CommType CommType::Deserialize(const Buffer& buffer)
{
	return CommType::Deserialize(buffer.get(), buffer.size());
}







